package com.opal.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.opal.system.entity.table.OpalPermission;
import com.opal.system.entity.table.OpalPermissionExample;
import com.opal.system.mapper.mysql.OpalPermissionMapper;
import com.opal.system.mapper.mysql.OpalPermissionMapperExtend;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
public class ShiroConfig {

	//Shiro生命周期处理器(配置注解驱动)如果这个配置出现在类中，那么该类的autowired都会失效:请在方法参数中使用@Qualifier
	@Bean(name = "lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	@Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        autoProxyCreator.setProxyTargetClass(true);
        return autoProxyCreator;
    }
	
	/**
	    *  开启shiro aop注解支持.
	    *  使用代理方式;所以需要开启代码支持;
	    * @param securityManager
	    * @return
	 */   
	   @Bean
	   public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("dwsm")SecurityManager securityManager){
	      AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
	      authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
	      return authorizationAttributeSourceAdvisor;
	   }

    
	//密码匹配器
	@Bean("credentialsMatcher")
	public HashedCredentialsMatcher setHashedCredentialsMatcher() {
		HashedCredentialsMatcher hcm= new HashedCredentialsMatcher();
		hcm.setHashAlgorithmName("md5");
		hcm.setHashIterations(2);
		hcm.setStoredCredentialsHexEncoded(true);
		return hcm;
	}
	
	@Bean(name = "myRealm")
	public UserRealm setMyRealm(@Qualifier("credentialsMatcher")HashedCredentialsMatcher credentialsMatcher) {
		UserRealm realm=new UserRealm();
		realm.setCredentialsMatcher(credentialsMatcher);
		realm.setCacheManager(new MemoryConstrainedCacheManager()); //开启缓存
		return realm;
	}

	//@Qualifier 注入spring容器中的bean
	@Bean(name = "dwsm")
	public SecurityManager setSecurityManager
								(@Qualifier("myRealm") UserRealm myRealm) { //使用属性@Qualifier("myRealm")获取第二步的bean
		DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
		dwsm.setRealm(myRealm); //管理第二步属性
		return dwsm;
	}
	

    
	/**
	 * Shiro内置过滤器，可以实现权限相关的拦截器 常用的过滤器： 
	 * anon: 无需认证（登录）可以访问 
	 * authc: 必须认证才可以访问
	 * user:如果使用rememberMe的功能可以直接访问 
	 * perms： 该资源必须得到资源权限才可以访问
	 * role: 该资源必须得到角色权限才可以访问
	 */

	@Bean
	public ShiroFilterFactoryBean setShiroFilterFactoryBean(@Qualifier("dwsm") SecurityManager dwsm,OpalPermissionMapper opalPermissionMapper) {
		ShiroFilterFactoryBean sffb = new ShiroFilterFactoryBean();
		// 注入安全管理器
		sffb.setSecurityManager(dwsm);
		// 配置拦截规则，使用LinkedHashMap 让key排序
		Map<String, String> map = new LinkedHashMap<String, String>();
		map.put("/user/login", "anon");
		map.put("/user/login.html", "anon");
		map.put("/user/permissionDenied.html", "anon");
		map.put("/login.html", "anon");
		map.put("/css/**", "anon");
		map.put("/js/**", "anon");
		map.put("/json/**", "anon");

		//需要授权的配置
		List<OpalPermission> opalPermissions = opalPermissionMapper.selectByExample(new OpalPermissionExample());
		for (OpalPermission op:opalPermissions ){
			String urlPath = op.getPermissionUrl();
			String permissionCode = op.getPermissionCode();
			//设置需要权限的路径：key：路径(不能为空)，value：权限名称
			if(!StringUtils.isBlank(urlPath)){
				map.put(urlPath,"perms["+permissionCode+"]");
			}else {
				System.out.println("启动失败：urlPath为空或null");
			}

		}
		//map.put("/manage/**","perms[manage]");
		//map.put("/salesorg/**","perms[salesorg]");
		// 配置除了上面的login可以不用认证外，其它资源都要认证
		map.put("/**", "authc");
		//认证授权map集合
		sffb.setFilterChainDefinitionMap(map);

		// 配置认证(登录)失败跳转页面
		//注意 若前面未带反斜杠 /  网页会提示重定向循环
		sffb.setLoginUrl("/user/tologin");
		//没有权限跳转页面
		sffb.setUnauthorizedUrl("/user/permissionDenied.html");

		return sffb;
	}
	
	/**
	 * 配置ShiroDialect，用于thymeleaf和shiro标签配合使用
	 */
	@Bean
	public ShiroDialect getShiroDialect(){
		return new ShiroDialect();
	}


}
